1mod chain_reaction;
2mod cx;
3mod cz;
4mod nvl;
5
6use super::Entry;
7use super::archive::*;
8use crate::ext::io::*;
9use crate::scripts::base::*;
10use crate::types::*;
11use crate::utils::case_insensitive_string::*;
12use crate::utils::encoding::*;
13use crate::utils::serde_base64bytes::*;
14use crate::utils::simple_pack::*;
15use anyhow::Result;
16use msg_tool_xp3data::*;
17use overf::wrapping as w;
18use serde::Deserialize;
19use std::collections::{BTreeMap, HashMap};
20use std::io::{Read, Seek, SeekFrom};
21use std::sync::Arc;
22
23pub use cx::Hxv4Crypt;
24
25type CIS = CaseInsensitiveStr;
26
27pub fn default_init_crypt(archive: &mut Xp3Archive) -> Result<()> {
28 if archive.extras.iter().any(|extra| extra.is_filename_hash()) {
29 let mut filename_map = HashMap::new();
30 for extra in &archive.extras {
31 if extra.is_filename_hash() {
32 let mut reader = MemReaderRef::new(&extra.data);
33 let hash = reader.read_u32()?;
34 let name_length = reader.read_u16()?;
35 let name = reader.read_exact_vec(name_length as usize * 2)?;
36 let name = decode_to_string(Encoding::Utf16LE, &name, true)?;
37 filename_map.insert(hash, name);
38 }
39 }
40 archive.extras.retain(|extra| !extra.is_filename_hash());
41 for entry in &mut archive.entries {
42 if let Some(name) = filename_map.get(&entry.file_hash) {
43 entry.name = name.clone();
44 }
45 }
46 }
47 Ok(())
48}
49
50fn default_read_name<'a>(reader: &mut Box<dyn Read + 'a>) -> Result<(String, u64)> {
51 let name_length = reader.read_u16()?;
52 let name = reader.read_exact_vec(name_length as usize * 2)?;
53 Ok((
54 decode_to_string(Encoding::Utf16LE, &name, true)?,
55 name_length as u64 * 2 + 2,
56 ))
57}
58
59pub trait Crypt: std::fmt::Debug {
60 #[allow(dead_code)]
61 fn hash_after_crypt(&self) -> bool;
63
64 fn startup_tjs_not_encrypted(&self) -> bool;
66
67 #[allow(dead_code)]
71 fn obfuscated_index(&self) -> bool;
72
73 fn init(&self, archive: &mut Xp3Archive) -> Result<()> {
75 default_init_crypt(archive)
76 }
77
78 fn read_name<'a>(&self, reader: &mut Box<dyn Read + 'a>) -> Result<(String, u64)> {
80 default_read_name(reader)
81 }
82
83 fn decrypt<'a>(
85 &self,
86 _entry: &Xp3Entry,
87 _cur_seg: &Segment,
88 _stream: Box<dyn Read + Send + Sync + 'a>,
89 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
90 Err(anyhow::anyhow!("This crypt does not support decrypt"))
91 }
92
93 fn decrypt_with_seek<'a>(
95 &self,
96 _entry: &Xp3Entry,
97 _cur_seg: &Segment,
98 _stream: Box<dyn ReadSeek + Send + Sync + 'a>,
99 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
100 Err(anyhow::anyhow!(
101 "This crypt does not support decrypt with seek"
102 ))
103 }
104
105 fn decrypt_supported(&self) -> bool {
107 false
108 }
109
110 fn decrypt_seek_supported(&self) -> bool {
112 false
113 }
114
115 fn need_filter(&self, _filename: &str, _buf: &[u8], _buf_len: usize) -> bool {
117 false
118 }
119
120 fn filter_seek_supported(&self) -> bool {
122 false
123 }
124
125 fn filter<'a>(&self, _entry: Entry<'a>) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
127 Err(anyhow::anyhow!(
128 "This crypt does not support content filter after decrypt"
129 ))
130 }
131
132 fn filter_with_seek<'a>(
134 &self,
135 _entry: Entry<'a>,
136 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
137 Err(anyhow::anyhow!(
138 "This crypt does not support content filter with seek after decrypt"
139 ))
140 }
141}
142
143#[derive(Clone, Debug, Deserialize)]
144#[serde(rename_all = "PascalCase")]
145pub struct CxSchema {
146 mask: u32,
147 offset: u32,
148 prolog_order: Base64Bytes,
149 odd_branch_order: Base64Bytes,
150 even_branch_order: Base64Bytes,
151 control_block_name: Option<String>,
152 tpm_file_name: Option<String>,
153}
154
155#[derive(Clone, Debug, Deserialize)]
156#[serde(rename_all = "PascalCase", tag = "$type")]
157enum CryptType {
158 NoCrypt,
159 FateCrypt,
160 MizukakeCrypt,
161 HashCrypt,
162 #[serde(rename_all = "PascalCase")]
163 XorCrypt {
164 key: u8,
165 },
166 FlyingShineCrypt,
167 CxEncryption(CxSchema),
168 #[serde(rename_all = "PascalCase")]
169 SenrenCxCrypt {
170 #[serde(flatten)]
171 cx: CxSchema,
172 names_section_id: String,
173 },
174 #[serde(rename_all = "PascalCase")]
175 CabbageCxCrypt {
176 #[serde(flatten)]
177 cx: CxSchema,
178 names_section_id: String,
179 random_seed: u32,
180 },
181 #[serde(rename_all = "PascalCase")]
182 NanaCxCrypt {
183 #[serde(flatten)]
184 cx: CxSchema,
185 names_section_id: String,
186 random_seed: u32,
187 yuz_key: Vec<u32>,
188 },
189 #[serde(rename_all = "PascalCase")]
190 RiddleCxCrypt {
191 #[serde(flatten)]
192 cx: CxSchema,
193 names_section_id: String,
194 random_seed: u32,
195 yuz_key: Vec<u32>,
196 #[serde(default)]
197 key1: u32,
198 #[serde(default)]
199 key2: u32,
200 },
201 SeitenCrypt,
202 OkibaCrypt,
203 DieselmineCrypt,
204 DameganeCrypt,
205 NephriteCrypt,
206 AlteredPinkCrypt,
207 NatsupochiCrypt,
208 PoringSoftCrypt,
209 AppliqueCrypt,
210 TokidokiCrypt,
211 SourireCrypt,
212 HibikiCrypt,
213 #[serde(rename_all = "PascalCase")]
214 AkabeiCrypt {
215 seed: u32,
216 },
217 HaikuoCrypt,
218 #[serde(rename_all = "PascalCase")]
219 StripeCrypt {
220 key: u8,
221 },
222 ExaCrypt,
223 #[serde(rename_all = "PascalCase")]
224 SmileCrypt {
225 key_xor: u32,
226 first_xor: u8,
227 zero_xor: u8,
228 },
229 YuzuCrypt,
230 HighRunningCrypt,
231 KissCrypt,
232 #[serde(rename_all = "PascalCase")]
233 PuCaCrypt {
234 hash_table: Vec<u32>,
235 key_table: Base64Bytes,
236 },
237 #[serde(rename_all = "PascalCase")]
238 RhapsodyCrypt {
239 file_list_name: String,
240 },
241 #[serde(rename_all = "PascalCase")]
242 MadoCrypt {
243 seed: u32,
244 },
245 #[serde(rename_all = "PascalCase")]
246 SmxCrypt {
247 mask: u32,
248 key_seq: Base64Bytes,
249 },
250 FestivalCrypt,
251 PinPointCrypt,
252 HybridCrypt,
253 #[serde(rename_all = "PascalCase")]
254 NekoWorksCrypt {
255 key: Base64Bytes,
256 },
257 #[serde(rename_all = "PascalCase")]
258 NinkiSeiyuuCrypt {
259 key1: u64,
260 key2: u64,
261 key3: u64,
262 },
263 SyangrilaSmartCrypt,
264 Kano2Crypt,
265 MiburoCrypt,
266 #[serde(rename_all = "PascalCase")]
267 PureMoreCrypt {
268 file_list_name: String,
269 char_map: String,
270 layer_name_suffix: String,
271 },
272 ChainReactionCrypt,
273 HachukanoCrypt,
274 ChocolatCrypt,
275 XanaduCrypt,
276 SisMikoCrypt,
277 #[serde(rename_all = "PascalCase")]
278 HxCryptLite {
279 #[serde(flatten)]
280 cx: CxSchema,
281 #[serde(default)]
282 header_key: Option<Base64Bytes>,
283 #[serde(default)]
284 header_split_position: u64,
285 #[serde(default)]
286 file_crypt_flag: bool,
287 #[serde(default)]
288 random_type: i32,
289 },
290 #[serde(rename_all = "PascalCase")]
291 HxCrypt {
292 #[serde(flatten)]
293 cx: CxSchema,
294 index_key1: cx::IndexKey,
295 index_key2: cx::IndexKeys,
296 filter_key: u64,
297 #[serde(default)]
298 random_type: i32,
299 #[serde(default)]
300 file_list_name: Option<String>,
301 },
302 #[serde(rename_all = "PascalCase")]
303 Hxv4Crypt {
304 key_packages: Vec<cx::KeyPackage>,
305 },
306 #[serde(rename_all = "PascalCase")]
307 Xor2Crypt {
308 key1: u8,
309 key2: u8,
310 },
311 LeaveSLeaveCrypt,
312 #[serde(rename_all = "PascalCase")]
313 NVLCrypt {
314 key: Base64Bytes,
315 },
316}
317
318#[derive(Clone, Debug, Deserialize)]
319#[serde(rename_all = "PascalCase")]
320#[allow(dead_code)]
321pub struct BaseSchema {
322 #[serde(default)]
323 hash_after_crypt: bool,
324 #[serde(default)]
325 startup_tjs_not_encrypted: bool,
326 #[serde(default)]
327 obfuscated_index: bool,
328}
329
330#[derive(Clone, Debug, Deserialize)]
331#[serde(rename_all = "PascalCase")]
332pub struct Schema {
333 #[serde(flatten)]
334 crypt: CryptType,
335 title: Option<String>,
336 #[serde(flatten)]
337 base: BaseSchema,
338}
339
340impl Schema {
341 pub fn create_crypt(
342 &self,
343 filename: &str,
344 config: &ExtraConfig,
345 ) -> Result<Box<dyn Crypt + Send + Sync>> {
346 Ok(match &self.crypt {
347 CryptType::NoCrypt => Box::new(NoCrypt::new()),
348 CryptType::FateCrypt => Box::new(FateCrypt::new(self.base.clone())),
349 CryptType::MizukakeCrypt => Box::new(MizukakeCrypt::new(self.base.clone())),
350 CryptType::HashCrypt => Box::new(HashCrypt::new(self.base.clone())),
351 CryptType::XorCrypt { key } => Box::new(XorCrypt::new(self.base.clone(), *key)),
352 CryptType::FlyingShineCrypt => Box::new(FlyingShineCrypt::new(self.base.clone())),
353 CryptType::CxEncryption(schema) => {
354 Box::new(cx::CxEncryption::new(self.base.clone(), &schema, filename)?)
355 }
356 CryptType::SenrenCxCrypt {
357 cx,
358 names_section_id,
359 } => Box::new(cx::SenrenCxCrypt::new(
360 self.base.clone(),
361 cx,
362 filename,
363 names_section_id.clone(),
364 )?),
365 CryptType::CabbageCxCrypt {
366 cx,
367 names_section_id,
368 random_seed,
369 } => Box::new(cx::CabbageCxCrypt::new(
370 self.base.clone(),
371 cx,
372 filename,
373 names_section_id.clone(),
374 *random_seed,
375 )?),
376 CryptType::NanaCxCrypt {
377 cx,
378 names_section_id,
379 random_seed,
380 yuz_key,
381 } => Box::new(cx::NanaCxCrypt::new(
382 self.base.clone(),
383 cx,
384 filename,
385 names_section_id.clone(),
386 *random_seed,
387 &yuz_key,
388 )?),
389 CryptType::RiddleCxCrypt {
390 cx,
391 names_section_id,
392 random_seed,
393 yuz_key,
394 key1,
395 key2,
396 } => Box::new(cx::RiddleCxCrypt::new(
397 self.base.clone(),
398 cx,
399 filename,
400 names_section_id.clone(),
401 *random_seed,
402 &yuz_key,
403 *key1,
404 *key2,
405 )?),
406 CryptType::SeitenCrypt => Box::new(SeitenCrypt::new(self.base.clone())),
407 CryptType::OkibaCrypt => Box::new(OkibaCrypt::new(self.base.clone())),
408 CryptType::DieselmineCrypt => Box::new(DieselmineCrypt::new(self.base.clone())),
409 CryptType::DameganeCrypt => Box::new(DameganeCrypt::new(self.base.clone())),
410 CryptType::NephriteCrypt => Box::new(NephriteCrypt::new(self.base.clone())),
411 CryptType::AlteredPinkCrypt => Box::new(AlteredPinkCrypt::new(self.base.clone())),
412 CryptType::NatsupochiCrypt => Box::new(NatsupochiCrypt::new(self.base.clone())),
413 CryptType::PoringSoftCrypt => Box::new(PoringSoftCrypt::new(self.base.clone())),
414 CryptType::AppliqueCrypt => Box::new(AppliqueCrypt::new(self.base.clone())),
415 CryptType::TokidokiCrypt => Box::new(TokidokiCrypt::new(self.base.clone())),
416 CryptType::SourireCrypt => Box::new(SourireCrypt::new(self.base.clone())),
417 CryptType::HibikiCrypt => Box::new(HibikiCrypt::new(self.base.clone())),
418 CryptType::AkabeiCrypt { seed } => Box::new(AkabeiCrypt::new(self.base.clone(), *seed)),
419 CryptType::HaikuoCrypt => Box::new(HaikuoCrypt::new(self.base.clone())),
420 CryptType::StripeCrypt { key } => Box::new(StripeCrypt::new(self.base.clone(), *key)),
421 CryptType::ExaCrypt => Box::new(ExaCrypt::new(self.base.clone())),
422 CryptType::SmileCrypt {
423 key_xor,
424 first_xor,
425 zero_xor,
426 } => Box::new(SmileCrypt::new(
427 self.base.clone(),
428 *key_xor,
429 *first_xor,
430 *zero_xor,
431 )),
432 CryptType::YuzuCrypt => Box::new(YuzuCrypt::new(self.base.clone())),
433 CryptType::HighRunningCrypt => Box::new(HighRunningCrypt::new(self.base.clone())),
434 CryptType::KissCrypt => Box::new(cz::KissCrypt::new(self.base.clone())),
435 CryptType::PuCaCrypt {
436 hash_table,
437 key_table,
438 } => Box::new(PuCaCrypt::new(
439 self.base.clone(),
440 hash_table.clone(),
441 key_table.bytes.clone(),
442 )?),
443 CryptType::RhapsodyCrypt { file_list_name } => Box::new(RhapsodyCrypt::new(
444 self.base.clone(),
445 &file_list_name,
446 config.xp3_file_list_path.as_ref().map(|s| s.as_str()),
447 )?),
448 CryptType::MadoCrypt { seed } => Box::new(MadoCrypt::new(self.base.clone(), *seed)),
449 CryptType::SmxCrypt { mask, key_seq } => {
450 Box::new(SmxCrypt::new(self.base.clone(), *mask, &key_seq.bytes)?)
451 }
452 CryptType::FestivalCrypt => Box::new(FestivalCrypt::new(self.base.clone())),
453 CryptType::PinPointCrypt => Box::new(PinPointCrypt::new(self.base.clone())),
454 CryptType::HybridCrypt => Box::new(HybridCrypt::new(self.base.clone())),
455 CryptType::NekoWorksCrypt { key } => {
456 Box::new(NekoWorksCrypt::new(self.base.clone(), key.bytes.clone())?)
457 }
458 CryptType::NinkiSeiyuuCrypt { key1, key2, key3 } => Box::new(NinkiSeiyuuCrypt::new(
459 self.base.clone(),
460 *key1,
461 *key2,
462 *key3,
463 )),
464 CryptType::SyangrilaSmartCrypt => Box::new(SyangrilaSmartCrypt::new(self.base.clone())),
465 CryptType::Kano2Crypt => Box::new(Kano2Crypt::new(self.base.clone())),
466 CryptType::MiburoCrypt => Box::new(MiburoCrypt::new(self.base.clone())),
467 CryptType::PureMoreCrypt {
468 file_list_name,
469 char_map,
470 layer_name_suffix,
471 } => Box::new(PureMoreCrypt::new(
472 self.base.clone(),
473 file_list_name,
474 config.xp3_file_list_path.as_ref().map(|s| s.as_str()),
475 char_map,
476 layer_name_suffix,
477 )?),
478 CryptType::ChainReactionCrypt => {
479 Box::new(chain_reaction::ChainReactionCrypt::new(self.base.clone()))
480 }
481 CryptType::HachukanoCrypt => {
482 Box::new(chain_reaction::HachukanoCrypt::new(self.base.clone()))
483 }
484 CryptType::ChocolatCrypt => {
485 Box::new(chain_reaction::ChocolatCrypt::new(self.base.clone()))
486 }
487 CryptType::XanaduCrypt => Box::new(chain_reaction::XanaduCrypt::new(self.base.clone())),
488 CryptType::SisMikoCrypt => {
489 Box::new(chain_reaction::SisMikoCrypt::new(self.base.clone()))
490 }
491 CryptType::HxCryptLite {
492 cx,
493 header_key,
494 header_split_position,
495 file_crypt_flag,
496 random_type,
497 } => Box::new(cx::HxCryptLite::new(
498 self.base.clone(),
499 cx,
500 filename,
501 header_key.as_ref().map(|s| s.bytes.clone()),
502 *header_split_position,
503 *file_crypt_flag,
504 *random_type,
505 )?),
506 CryptType::HxCrypt {
507 cx,
508 index_key1,
509 index_key2,
510 filter_key,
511 random_type,
512 file_list_name,
513 } => Box::new(cx::HxCrypt::new(
514 self.base.clone(),
515 cx,
516 index_key1,
517 index_key2,
518 *filter_key,
519 *random_type,
520 file_list_name.as_ref().map(|s| s.as_str()),
521 config.xp3_file_list_path.as_ref().map(|s| s.as_str()),
522 filename,
523 config,
524 )?),
525 CryptType::Hxv4Crypt { key_packages } => Box::new(cx::Hxv4Crypt::new2(
526 &key_packages,
527 config.xp3_game_title.as_deref().unwrap_or_default(),
528 filename,
529 config,
530 )?),
531 CryptType::Xor2Crypt { key1, key2 } => {
532 Box::new(Xor2Crypt::new(self.base.clone(), *key1, *key2))
533 }
534 CryptType::LeaveSLeaveCrypt => Box::new(LeaveSLeaveCrypt::new(self.base.clone())),
535 CryptType::NVLCrypt { key } => Box::new(nvl::NVLCrypt::new(self.base.clone(), &key.bytes)?),
536 })
537 }
538}
539
540lazy_static::lazy_static! {
541 static ref CRYPT_SCHEMA: BTreeMap<CaseInsensitiveString, Schema> = {
542 serde_json::from_str(&get_crypt_data()).expect("Failed to parse crypt.json")
543 };
544 static ref ALIAS_TABLE: HashMap<String, String> = {
545 let mut table = HashMap::new();
546 for (game, fulltitle) in get_supported_games_with_title() {
547 if let Some(title) = fulltitle {
548 let mut alias_count = 0usize;
549 for part in title.split("|") {
550 let alias = part.trim();
551 table.insert(alias.to_string(), game.to_string());
552 alias_count += 1;
553 }
554 if alias_count > 1 {
556 table.insert(title.to_string(), game.to_string());
557 }
558 }
559 }
560 table
561 };
562 static ref CX_CB_TABLE: HashMap<String, Vec<u32>> = {
563 let reader = MemReaderRef::new(CX_CB_DATA);
564 let mut pack = read_simple_pack(reader).expect("Failed to read cx_cb.pck");
565 let mut table = HashMap::new();
566 while let Some(mut entry) = pack.next().expect("Failed to read entry in cx_cb.pck") {
567 let mut list = Vec::with_capacity(0x400);
568 let errmsg = format!("Failed to read u32 in cx_cb.pck entry {}", entry.name);
569 for _ in 0..0x400 {
570 list.push(entry.read_u32().expect(&errmsg));
571 }
572 while !entry.is_eof() {
573 list.push(entry.read_u32().expect(&errmsg));
574 }
575 table.insert(entry.name.clone(), list);
576 }
577 table
578 };
579}
580
581pub fn query_filename_list(name: &str) -> Result<String> {
582 let reader = MemReaderRef::new(NAME_LIST_DATA);
583 let mut pack = read_simple_pack(reader)?;
584 while let Some(mut entry) = pack.next()? {
585 if entry.name == name {
586 let mut str = String::new();
587 entry.read_to_string(&mut str)?;
588 return Ok(str);
589 }
590 }
591 Err(anyhow::anyhow!("Name list entry not found: {}", name))
592}
593
594pub fn get_supported_games() -> Vec<&'static str> {
596 CRYPT_SCHEMA.keys().map(|s| s.as_str()).collect()
597}
598
599pub fn get_supported_games_with_title() -> Vec<(&'static str, Option<&'static str>)> {
601 CRYPT_SCHEMA
602 .iter()
603 .map(|(k, v)| (k.as_str(), v.title.as_deref()))
604 .collect()
605}
606
607pub fn query_crypt_schema(game: &str) -> Option<&'static Schema> {
608 CRYPT_SCHEMA.get(CIS::from_str(game)).or_else(|| {
609 ALIAS_TABLE
610 .get(game)
611 .and_then(|real_game| CRYPT_SCHEMA.get(CIS::from_str(real_game)))
612 })
613}
614
615#[derive(Debug)]
616pub struct NoCrypt {}
617
618impl NoCrypt {
619 pub fn new() -> Self {
620 Self {}
621 }
622}
623
624impl Crypt for NoCrypt {
625 fn hash_after_crypt(&self) -> bool {
626 false
627 }
628 fn startup_tjs_not_encrypted(&self) -> bool {
629 false
630 }
631 fn obfuscated_index(&self) -> bool {
632 false
633 }
634}
635
636macro_rules! seek_impl {
637 ($reader:ident<$t:ident>) => {
638 impl<$t: Read + Seek> Seek for $reader<$t> {
639 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
640 let new_pos: i64 = match pos {
641 SeekFrom::Start(offset) => offset as i64,
642 SeekFrom::End(offset) => self.seg_size as i64 + offset,
643 SeekFrom::Current(offset) => self.pos as i64 + offset,
644 };
645 let offset = new_pos - self.pos as i64;
646 if offset != 0 {
647 self.inner.seek(SeekFrom::Current(offset))?;
648 self.pos = new_pos as u64;
649 }
650 Ok(self.pos)
651 }
652 }
653 };
654}
655
656macro_rules! seek_reader_impl {
657 ($reader:ident<$t:ident>) => {
658 #[derive(msg_tool_macro::MyDebug)]
659 struct $reader<$t: Read> {
660 #[skip_fmt]
661 inner: $t,
662 #[allow(unused)]
663 seg_start: u64,
665 seg_size: u64,
666 pos: u64,
667 }
668 impl<$t: Read> $reader<$t> {
669 pub fn new(inner: $t, seg: &Segment) -> Self {
670 Self {
671 inner,
672 seg_start: seg.offset_in_file,
673 seg_size: seg.original_size,
674 pos: 0,
675 }
676 }
677 }
678 seek_impl!($reader<$t>);
679 };
680}
681
682macro_rules! seek_reader_key_impl {
683 ($reader:ident<$t:ident>, $key:ty) => {
684 #[derive(msg_tool_macro::MyDebug)]
685 #[allow(dead_code)]
686 struct $reader<$t: Read> {
687 #[skip_fmt]
688 inner: $t,
689 seg_start: u64,
691 seg_size: u64,
692 pos: u64,
693 key: $key,
694 }
695 impl<$t: Read> $reader<$t> {
696 pub fn new(inner: $t, seg: &Segment, key: $key) -> Self {
697 Self {
698 inner,
699 seg_start: seg.offset_in_file,
700 seg_size: seg.original_size,
701 pos: 0,
702 key,
703 }
704 }
705 }
706 seek_impl!($reader<$t>);
707 };
708}
709
710macro_rules! base_schema_impl {
711 () => {
712 fn hash_after_crypt(&self) -> bool {
713 self.base.hash_after_crypt
714 }
715 fn startup_tjs_not_encrypted(&self) -> bool {
716 self.base.startup_tjs_not_encrypted
717 }
718 fn obfuscated_index(&self) -> bool {
719 self.base.obfuscated_index
720 }
721 };
722}
723
724macro_rules! base_schema2_impl {
725 () => {
726 fn hash_after_crypt(&self) -> bool {
727 AsRef::<BaseSchema>::as_ref(self).hash_after_crypt
728 }
729 fn startup_tjs_not_encrypted(&self) -> bool {
730 AsRef::<BaseSchema>::as_ref(self).startup_tjs_not_encrypted
731 }
732 fn obfuscated_index(&self) -> bool {
733 AsRef::<BaseSchema>::as_ref(self).obfuscated_index
734 }
735 };
736}
737
738macro_rules! seek_crypt_base_impl {
739 ($crypt:ident, $reader:ident) => {
740 #[derive(Debug)]
741 pub struct $crypt {
742 base: BaseSchema,
743 }
744 impl $crypt {
745 pub fn new(base: BaseSchema) -> Self {
746 Self { base }
747 }
748 }
749 impl Crypt for $crypt {
750 base_schema_impl!();
751 fn decrypt_supported(&self) -> bool {
752 true
753 }
754 fn decrypt_seek_supported(&self) -> bool {
755 true
756 }
757 fn decrypt<'a>(
758 &self,
759 _entry: &Xp3Entry,
760 cur_seg: &Segment,
761 stream: Box<dyn Read + Send + Sync + 'a>,
762 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
763 Ok(Box::new($reader::new(stream, cur_seg)))
764 }
765 fn decrypt_with_seek<'a>(
766 &self,
767 _entry: &Xp3Entry,
768 cur_seg: &Segment,
769 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
770 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
771 Ok(Box::new($reader::new(stream, cur_seg)))
772 }
773 }
774 };
775}
776
777macro_rules! seek_crypt_impl {
778 ($crypt:ident, $reader:ident<$t:ident>) => {
779 seek_crypt_base_impl!($crypt, $reader);
780 seek_reader_impl!($reader<$t>);
781 };
782}
783
784seek_crypt_impl!(FateCrypt, FateCryptReader<T>);
785
786impl<R: Read> Read for FateCryptReader<R> {
787 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
788 const XOR1_OFFSET: u64 = 0x13;
789 const XOR3_OFFSET: u64 = 0x2ea29;
790 let readed = self.inner.read(buf)?;
791 for (i, t) in (&mut buf[..readed]).iter_mut().enumerate() {
792 let tpos = self.seg_start + self.pos + i as u64;
793 *t ^= 0x36;
794 if tpos == XOR1_OFFSET {
795 *t ^= 0x1;
796 } else if tpos == XOR3_OFFSET {
797 *t ^= 0x3;
798 }
799 }
800 self.pos += readed as u64;
801 Ok(readed)
802 }
803}
804
805seek_crypt_impl!(MizukakeCrypt, MizukakeCryptReader<T>);
806
807impl<R: Read> Read for MizukakeCryptReader<R> {
808 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
809 let readed = self.inner.read(buf)?;
810 for (i, t) in (&mut buf[..readed]).iter_mut().enumerate() {
811 let tpos = self.seg_start + self.pos + i as u64;
812 if tpos == 0x103 {
813 *t = (*t).wrapping_sub(1);
814 }
815 *t ^= 0xb6;
816 if tpos == 0x3F82 {
817 *t ^= 1;
818 }
819 if tpos == 0x83 {
820 *t ^= 3;
821 }
822 }
823 self.pos += readed as u64;
824 Ok(readed)
825 }
826}
827
828macro_rules! seek_crypt_filehash_key_u8_base_impl {
829 ($crypt:ident, $reader:ident) => {
830 #[derive(Debug)]
831 pub struct $crypt {
832 base: BaseSchema,
833 }
834 impl $crypt {
835 pub fn new(base: BaseSchema) -> Self {
836 Self { base }
837 }
838 }
839 impl Crypt for $crypt {
840 base_schema_impl!();
841 fn decrypt_supported(&self) -> bool {
842 true
843 }
844 fn decrypt_seek_supported(&self) -> bool {
845 true
846 }
847 fn decrypt<'a>(
848 &self,
849 entry: &Xp3Entry,
850 cur_seg: &Segment,
851 stream: Box<dyn Read + Send + Sync + 'a>,
852 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
853 Ok(Box::new($reader::new(
854 stream,
855 cur_seg,
856 entry.file_hash as u8,
857 )))
858 }
859 fn decrypt_with_seek<'a>(
860 &self,
861 entry: &Xp3Entry,
862 cur_seg: &Segment,
863 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
864 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
865 Ok(Box::new($reader::new(
866 stream,
867 cur_seg,
868 entry.file_hash as u8,
869 )))
870 }
871 }
872 };
873}
874
875macro_rules! seek_crypt_filehash_key_u8_impl {
876 ($crypt:ident,$reader:ident<$t:ident>) => {
877 seek_crypt_filehash_key_u8_base_impl!($crypt, $reader);
878 seek_reader_key_impl!($reader<$t>, u8);
879 };
880}
881
882seek_crypt_filehash_key_u8_impl!(HashCrypt, HashCryptReader<T>);
883
884impl<R: Read> Read for HashCryptReader<R> {
885 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
886 let readed = self.inner.read(buf)?;
887 for t in (&mut buf[..readed]).iter_mut() {
888 *t ^= self.key;
889 }
890 self.pos += readed as u64;
891 Ok(readed)
892 }
893}
894
895macro_rules! seek_crypt_key_base_impl {
896 ($crypt:ident, $reader:ident, $typ:ty) => {
897 #[derive(Debug)]
898 pub struct $crypt {
899 base: BaseSchema,
900 key: $typ,
901 }
902 impl $crypt {
903 pub fn new(base: BaseSchema, key: $typ) -> Self {
904 Self { base, key }
905 }
906 }
907 impl Crypt for $crypt {
908 base_schema_impl!();
909 fn decrypt_supported(&self) -> bool {
910 true
911 }
912 fn decrypt_seek_supported(&self) -> bool {
913 true
914 }
915 fn decrypt<'a>(
916 &self,
917 _entry: &Xp3Entry,
918 cur_seg: &Segment,
919 stream: Box<dyn Read + Send + Sync + 'a>,
920 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
921 Ok(Box::new($reader::new(stream, cur_seg, self.key)))
922 }
923 fn decrypt_with_seek<'a>(
924 &self,
925 _entry: &Xp3Entry,
926 cur_seg: &Segment,
927 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
928 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
929 Ok(Box::new($reader::new(stream, cur_seg, self.key)))
930 }
931 }
932 };
933}
934
935macro_rules! seek_crypt_key_impl {
936 ($crypt:ident, $reader:ident<$t:ident>, $typ:ty) => {
937 seek_crypt_key_base_impl!($crypt, $reader, $typ);
938 seek_reader_key_impl!($reader<$t>, $typ);
939 };
940}
941
942seek_crypt_key_impl!(XorCrypt, XorCryptReader<T>, u8);
943
944impl<R: Read> Read for XorCryptReader<R> {
945 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
946 let readed = self.inner.read(buf)?;
947 for t in (&mut buf[..readed]).iter_mut() {
948 *t ^= self.key;
949 }
950 self.pos += readed as u64;
951 Ok(readed)
952 }
953}
954
955#[derive(Debug)]
956pub struct FlyingShineCrypt {
957 base: BaseSchema,
958}
959
960impl FlyingShineCrypt {
961 pub fn new(base: BaseSchema) -> Self {
962 Self { base }
963 }
964
965 fn adjust(&self, hash: u32) -> (u8, u32) {
966 let mut shift = hash & 0xFF;
967 if shift == 0 {
968 shift = 0xF;
969 }
970 let mut key = ((hash >> 8) & 0xFF) as u8;
971 if key == 0 {
972 key = 0xF0;
973 }
974 (key, shift)
975 }
976}
977
978impl Crypt for FlyingShineCrypt {
979 base_schema_impl!();
980 fn decrypt_supported(&self) -> bool {
981 true
982 }
983 fn decrypt_seek_supported(&self) -> bool {
984 true
985 }
986 fn decrypt<'a>(
987 &self,
988 entry: &Xp3Entry,
989 cur_seg: &Segment,
990 stream: Box<dyn Read + Send + Sync + 'a>,
991 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
992 Ok(Box::new(FlyingShineCryptReader::new(
993 stream,
994 cur_seg,
995 self.adjust(entry.file_hash),
996 )))
997 }
998 fn decrypt_with_seek<'a>(
999 &self,
1000 entry: &Xp3Entry,
1001 cur_seg: &Segment,
1002 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
1003 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
1004 Ok(Box::new(FlyingShineCryptReader::new(
1005 stream,
1006 cur_seg,
1007 self.adjust(entry.file_hash),
1008 )))
1009 }
1010}
1011
1012seek_reader_key_impl!(FlyingShineCryptReader<T>, (u8, u32));
1013
1014impl<R: Read> Read for FlyingShineCryptReader<R> {
1015 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1016 let (xor, shift) = self.key;
1017 let readed = self.inner.read(buf)?;
1018 for t in (&mut buf[..readed]).iter_mut() {
1019 *t ^= xor;
1020 *t = t.rotate_right(shift);
1021 }
1022 self.pos += readed as u64;
1023 Ok(readed)
1024 }
1025}
1026
1027macro_rules! seek_crypt_filehash_key_base_impl {
1028 ($crypt:ident, $reader:ident) => {
1029 #[derive(Debug)]
1030 pub struct $crypt {
1031 base: BaseSchema,
1032 }
1033 impl $crypt {
1034 pub fn new(base: BaseSchema) -> Self {
1035 Self { base }
1036 }
1037 }
1038 impl Crypt for $crypt {
1039 base_schema_impl!();
1040 fn decrypt_supported(&self) -> bool {
1041 true
1042 }
1043 fn decrypt_seek_supported(&self) -> bool {
1044 true
1045 }
1046 fn decrypt<'a>(
1047 &self,
1048 entry: &Xp3Entry,
1049 cur_seg: &Segment,
1050 stream: Box<dyn Read + Send + Sync + 'a>,
1051 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
1052 Ok(Box::new($reader::new(stream, cur_seg, entry.file_hash)))
1053 }
1054 fn decrypt_with_seek<'a>(
1055 &self,
1056 entry: &Xp3Entry,
1057 cur_seg: &Segment,
1058 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
1059 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
1060 Ok(Box::new($reader::new(stream, cur_seg, entry.file_hash)))
1061 }
1062 }
1063 };
1064}
1065
1066macro_rules! seek_crypt_filehash_key_impl {
1067 ($crypt:ident,$reader:ident<$t:ident>) => {
1068 seek_crypt_filehash_key_base_impl!($crypt, $reader);
1069 seek_reader_key_impl!($reader<$t>, u32);
1070 };
1071}
1072
1073seek_crypt_filehash_key_impl!(SeitenCrypt, SeitenCryptReader<T>);
1074
1075impl<R: Read> Read for SeitenCryptReader<R> {
1076 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1077 let readed = self.inner.read(buf)?;
1078 let mut offset = self.seg_start + self.pos;
1079 for t in (&mut buf[..readed]).iter_mut() {
1080 let mut shift;
1081 let key = self.key ^ (offset as u32);
1082 if key & 2 != 0 {
1083 shift = key & 0x18;
1084 let ebx = key >> shift;
1085 shift &= 8;
1086 *t ^= (ebx | (key >> shift)) as u8;
1087 }
1088 if key & 4 != 0 {
1089 w!(*t += key as u8);
1090 }
1091 if key & 8 != 0 {
1092 shift = key & 0x10;
1093 w!(*t -= (key >> shift) as u8);
1094 }
1095 offset += 1;
1096 }
1097 self.pos += readed as u64;
1098 Ok(readed)
1099 }
1100}
1101
1102seek_crypt_filehash_key_impl!(OkibaCrypt, OkibaCryptReader<T>);
1103
1104impl<R: Read> Read for OkibaCryptReader<R> {
1105 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1106 let readed = self.inner.read(buf)?;
1107 let mut offset = self.seg_start + self.pos;
1108 let mut i = 0;
1109 if offset < 0x65 {
1110 let key = self.key >> 4;
1111 let limit = readed.min(0x65 - offset as usize);
1112 for _ in 0..limit {
1113 buf[i] ^= key as u8;
1114 i += 1;
1115 offset += 1;
1116 }
1117 }
1118 if i < readed {
1119 offset -= 0x65;
1120 let mut key = self.key;
1121 key = ((key & 0xff0000) << 8)
1122 | ((key & 0xff000000) >> 8)
1123 | ((key & 0xff00) >> 8)
1124 | ((key & 0xff) << 8);
1125 loop {
1126 buf[i] ^= (key >> (8 * (offset as u32 & 3))) as u8;
1127 offset += 1;
1128 i += 1;
1129 if i >= readed {
1130 break;
1131 }
1132 }
1133 }
1134 self.pos += readed as u64;
1135 Ok(readed)
1136 }
1137}
1138
1139seek_crypt_filehash_key_u8_impl!(DieselmineCrypt, DieselmineCryptReader<T>);
1140
1141impl<R: Read> Read for DieselmineCryptReader<R> {
1142 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1143 let readed = self.inner.read(buf)?;
1144 let key = self.key as i32;
1145 for (i, t) in (&mut buf[..readed]).iter_mut().enumerate() {
1146 let offset = self.seg_start + self.pos + i as u64;
1147 let key = if offset < 123 {
1148 21 * key
1149 } else if offset < 246 {
1150 -32 * key
1151 } else if offset < 369 {
1152 43 * key
1153 } else {
1154 -54 * key
1155 } as u8;
1156 *t ^= key;
1157 }
1158 self.pos += readed as u64;
1159 Ok(readed)
1160 }
1161}
1162
1163seek_crypt_filehash_key_u8_impl!(DameganeCrypt, DameganeCryptReader<T>);
1164
1165impl<R: Read> Read for DameganeCryptReader<R> {
1166 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1167 let readed = self.inner.read(buf)?;
1168 for (i, t) in (&mut buf[..readed]).iter_mut().enumerate() {
1169 let offset = self.seg_start + self.pos + i as u64;
1170 let key = if offset & 1 != 0 {
1171 self.key
1172 } else {
1173 offset as u8
1174 };
1175 *t ^= key;
1176 }
1177 self.pos += readed as u64;
1178 Ok(readed)
1179 }
1180}
1181
1182seek_crypt_filehash_key_u8_impl!(NephriteCrypt, NephriteCryptReader<T>);
1183
1184impl<R: Read> Read for NephriteCryptReader<R> {
1185 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1186 let readed = self.inner.read(buf)?;
1187 for (i, t) in (&mut buf[..readed]).iter_mut().enumerate() {
1188 let offset = self.seg_start + self.pos + i as u64;
1189 let key = if offset & 1 == 0 {
1190 self.key
1191 } else {
1192 offset as u8
1193 };
1194 *t ^= key;
1195 }
1196 self.pos += readed as u64;
1197 Ok(readed)
1198 }
1199}
1200
1201seek_crypt_impl!(AlteredPinkCrypt, AlteredPinkCryptReader<T>);
1202
1203impl<R: Read> Read for AlteredPinkCryptReader<R> {
1204 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1205 let readed = self.inner.read(buf)?;
1206 for (i, t) in (&mut buf[..readed]).iter_mut().enumerate() {
1207 let offset = self.seg_start + self.pos + i as u64;
1208 *t ^= ALTERED_PINK_KEY_TABLE[(offset & 0xFF) as usize];
1209 }
1210 self.pos += readed as u64;
1211 Ok(readed)
1212 }
1213}
1214
1215seek_crypt_filehash_key_impl!(NatsupochiCrypt, NatsupochiCryptReader<T>);
1216
1217impl<R: Read> Read for NatsupochiCryptReader<R> {
1218 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1219 let readed = self.inner.read(buf)?;
1220 let key = (self.key >> 3) as u8;
1221 for t in (&mut buf[..readed]).iter_mut() {
1222 *t ^= key;
1223 }
1224 self.pos += readed as u64;
1225 Ok(readed)
1226 }
1227}
1228
1229seek_crypt_filehash_key_impl!(PoringSoftCrypt, PoringSoftCryptReader<T>);
1230
1231impl<R: Read> Read for PoringSoftCryptReader<R> {
1232 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1233 let readed = self.inner.read(buf)?;
1234 let key = (!w!(self.key + 1)) as u8;
1235 for t in (&mut buf[..readed]).iter_mut() {
1236 *t ^= key;
1237 }
1238 self.pos += readed as u64;
1239 Ok(readed)
1240 }
1241}
1242
1243seek_crypt_filehash_key_impl!(AppliqueCrypt, AppliqueCryptReader<T>);
1244
1245impl<R: Read> Read for AppliqueCryptReader<R> {
1246 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1247 let readed = self.inner.read(buf)?;
1248 let key = (self.key >> 12) as u8;
1249 let skip = (5 - (self.seg_start + self.pos).min(5) as usize).min(readed);
1250 for t in (&mut buf[skip..readed]).iter_mut() {
1251 *t ^= key;
1252 }
1253 self.pos += readed as u64;
1254 Ok(readed)
1255 }
1256}
1257
1258#[derive(Debug)]
1259pub struct TokidokiCrypt {
1260 base: BaseSchema,
1261}
1262
1263impl TokidokiCrypt {
1264 pub fn new(base: BaseSchema) -> Self {
1265 Self { base }
1266 }
1267
1268 fn get_key(&self, entry: &Xp3Entry) -> Result<(u64, u32)> {
1270 let ext = entry
1271 .name
1272 .rsplit('.')
1273 .next()
1274 .unwrap_or("")
1275 .to_ascii_lowercase();
1276 if !ext.is_empty() {
1277 let ext = format!(".{}", ext);
1278 let mut ext_bin = encode_string(Encoding::Cp932, &ext, true)?;
1279 ext_bin.resize(4, 0);
1280 let mut reader = MemReaderRef::new(&ext_bin);
1281 let key = !reader.read_u32()?;
1282 if ext == ".asd" || ext == ".ks" || ext == ".tjs" {
1283 Ok((entry.original_size, key))
1284 } else {
1285 Ok((entry.original_size.min(0x100), key))
1286 }
1287 } else {
1288 Ok((entry.original_size.min(0x100), u32::MAX))
1289 }
1290 }
1291}
1292
1293impl Crypt for TokidokiCrypt {
1294 base_schema_impl!();
1295 fn decrypt_supported(&self) -> bool {
1296 true
1297 }
1298 fn decrypt_seek_supported(&self) -> bool {
1299 true
1300 }
1301 fn decrypt<'a>(
1302 &self,
1303 entry: &Xp3Entry,
1304 cur_seg: &Segment,
1305 stream: Box<dyn Read + Send + Sync + 'a>,
1306 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
1307 Ok(Box::new(TokidokiCryptReader::new(
1308 stream,
1309 cur_seg,
1310 self.get_key(entry)?,
1311 )))
1312 }
1313 fn decrypt_with_seek<'a>(
1314 &self,
1315 entry: &Xp3Entry,
1316 cur_seg: &Segment,
1317 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
1318 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
1319 Ok(Box::new(TokidokiCryptReader::new(
1320 stream,
1321 cur_seg,
1322 self.get_key(entry)?,
1323 )))
1324 }
1325}
1326
1327seek_reader_key_impl!(TokidokiCryptReader<T>, (u64, u32));
1328
1329impl<R: Read> Read for TokidokiCryptReader<R> {
1330 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1331 let (limit, key) = self.key;
1332 let readed = self.inner.read(buf)?;
1333 for (i, t) in (&mut buf[..readed]).iter_mut().enumerate() {
1334 let offset = self.seg_start + self.pos + i as u64;
1335 if offset < limit {
1336 *t ^= (key >> ((offset as i32 & 3) << 3)) as u8;
1337 }
1338 }
1339 self.pos += readed as u64;
1340 Ok(readed)
1341 }
1342}
1343
1344seek_crypt_filehash_key_impl!(SourireCrypt, SourireCryptReader<T>);
1345
1346impl<R: Read> Read for SourireCryptReader<R> {
1347 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1348 let readed = self.inner.read(buf)?;
1349 let key = (self.key ^ 0xCD) as u8;
1350 for t in (&mut buf[..readed]).iter_mut() {
1351 *t ^= key;
1352 }
1353 self.pos += readed as u64;
1354 Ok(readed)
1355 }
1356}
1357
1358seek_crypt_filehash_key_impl!(HibikiCrypt, HibikiCryptReader<T>);
1359
1360impl<R: Read> Read for HibikiCryptReader<R> {
1361 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1362 let readed = self.inner.read(buf)?;
1363 let key1 = (self.key >> 5) as u8;
1364 let key2 = (self.key >> 8) as u8;
1365 for (i, t) in (&mut buf[..readed]).iter_mut().enumerate() {
1366 let offset = self.seg_start + self.pos + i as u64;
1367 let key = if offset <= 0x64 || offset & 4 != 0 {
1368 key1
1369 } else {
1370 key2
1371 };
1372 *t ^= key;
1373 }
1374 self.pos += readed as u64;
1375 Ok(readed)
1376 }
1377}
1378
1379#[derive(Debug)]
1380pub struct AkabeiCrypt {
1381 base: BaseSchema,
1382 seed: u32,
1383}
1384
1385impl AkabeiCrypt {
1386 pub fn new(base: BaseSchema, seed: u32) -> Self {
1387 Self { base, seed }
1388 }
1389
1390 fn get_key(&self, mut hash: u32) -> [u8; 0x20] {
1391 let mut state = [0; 0x20];
1392 hash = (hash ^ self.seed) & 0x7FFFFFFF;
1393 hash = hash << 31 | hash;
1394 for i in 0..0x20 {
1395 state[i] = (hash & 0xFF) as u8;
1396 hash = (hash & 0xFFFFFFFE) << 23 | hash >> 8;
1397 }
1398 state
1399 }
1400}
1401
1402impl Crypt for AkabeiCrypt {
1403 base_schema_impl!();
1404 fn decrypt_supported(&self) -> bool {
1405 true
1406 }
1407 fn decrypt_seek_supported(&self) -> bool {
1408 true
1409 }
1410 fn decrypt<'a>(
1411 &self,
1412 entry: &Xp3Entry,
1413 cur_seg: &Segment,
1414 stream: Box<dyn Read + Send + Sync + 'a>,
1415 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
1416 Ok(Box::new(AkabeiCryptReader::new(
1417 stream,
1418 cur_seg,
1419 self.get_key(entry.file_hash),
1420 )))
1421 }
1422 fn decrypt_with_seek<'a>(
1423 &self,
1424 entry: &Xp3Entry,
1425 cur_seg: &Segment,
1426 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
1427 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
1428 Ok(Box::new(AkabeiCryptReader::new(
1429 stream,
1430 cur_seg,
1431 self.get_key(entry.file_hash),
1432 )))
1433 }
1434}
1435
1436seek_reader_key_impl!(AkabeiCryptReader<T>, [u8; 0x20]);
1437
1438impl<R: Read> Read for AkabeiCryptReader<R> {
1439 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1440 let readed = self.inner.read(buf)?;
1441 for (i, t) in (&mut buf[..readed]).iter_mut().enumerate() {
1442 let offset = self.seg_start + self.pos + i as u64;
1443 *t ^= self.key[(offset & 0x1F) as usize];
1444 }
1445 self.pos += readed as u64;
1446 Ok(readed)
1447 }
1448}
1449
1450seek_crypt_filehash_key_impl!(HaikuoCrypt, HaikuoCryptReader<T>);
1451
1452impl<R: Read> Read for HaikuoCryptReader<R> {
1453 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1454 let readed = self.inner.read(buf)?;
1455 let key = (self.key ^ (self.key >> 8)) as u8;
1456 for t in (&mut buf[..readed]).iter_mut() {
1457 *t ^= key;
1458 }
1459 self.pos += readed as u64;
1460 Ok(readed)
1461 }
1462}
1463
1464seek_crypt_key_impl!(StripeCrypt, StripeCryptReader<T>, u8);
1465
1466impl<R: Read> Read for StripeCryptReader<R> {
1467 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1468 let readed = self.inner.read(buf)?;
1469 for t in (&mut buf[..readed]).iter_mut() {
1470 *t ^= self.key;
1471 w!(*t += 1);
1472 }
1473 self.pos += readed as u64;
1474 Ok(readed)
1475 }
1476}
1477
1478seek_crypt_filehash_key_impl!(ExaCrypt, ExaCryptReader<T>);
1479
1480impl<R: Read> Read for ExaCryptReader<R> {
1481 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1482 let readed = self.inner.read(buf)?;
1483 let mut shift = ((self.seg_start + self.pos) % 5) as u32;
1484 for t in (&mut buf[..readed]).iter_mut() {
1485 *t ^= (self.key >> shift) as u8;
1486 shift = (shift + 1) % 5;
1487 }
1488 self.pos += readed as u64;
1489 Ok(readed)
1490 }
1491}
1492
1493#[derive(Debug)]
1494pub struct SmileCrypt {
1495 base: BaseSchema,
1496 key_xor: u32,
1497 first_xor: u8,
1498 zero_xor: u8,
1499}
1500
1501impl SmileCrypt {
1502 pub fn new(base: BaseSchema, key_xor: u32, first_xor: u8, zero_xor: u8) -> Self {
1503 Self {
1504 base,
1505 key_xor,
1506 first_xor,
1507 zero_xor,
1508 }
1509 }
1510}
1511
1512impl Crypt for SmileCrypt {
1513 base_schema_impl!();
1514 fn decrypt_supported(&self) -> bool {
1515 true
1516 }
1517 fn decrypt_seek_supported(&self) -> bool {
1518 true
1519 }
1520 fn decrypt<'a>(
1521 &self,
1522 entry: &Xp3Entry,
1523 cur_seg: &Segment,
1524 stream: Box<dyn Read + Send + Sync + 'a>,
1525 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
1526 let key = entry.file_hash ^ self.key_xor;
1527 Ok(Box::new(SmileCryptReader::new(
1528 stream,
1529 cur_seg,
1530 (key, self.first_xor, self.zero_xor),
1531 )))
1532 }
1533 fn decrypt_with_seek<'a>(
1534 &self,
1535 entry: &Xp3Entry,
1536 cur_seg: &Segment,
1537 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
1538 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
1539 let key = entry.file_hash ^ self.key_xor;
1540 Ok(Box::new(SmileCryptReader::new(
1541 stream,
1542 cur_seg,
1543 (key, self.first_xor, self.zero_xor),
1544 )))
1545 }
1546}
1547
1548seek_reader_key_impl!(SmileCryptReader<T>, (u32, u8, u8));
1549
1550impl<R: Read> Read for SmileCryptReader<R> {
1551 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1552 let readed = self.inner.read(buf)?;
1553 let (mut hash, first_xor, zero_xor) = self.key;
1554 let mut key = (hash ^ (hash >> 8) ^ (hash >> 16) ^ (hash >> 24)) as u8;
1555 if key == 0 {
1556 key = zero_xor;
1557 }
1558 if self.pos == 0 && self.seg_start == 0 && readed > 0 {
1559 if hash & 0xFF == 0 {
1560 hash = first_xor as u32;
1561 }
1562 buf[0] ^= hash as u8;
1563 }
1564 for t in (&mut buf[..readed]).iter_mut() {
1565 *t ^= key;
1566 }
1567 self.pos += readed as u64;
1568 Ok(readed)
1569 }
1570}
1571
1572seek_crypt_filehash_key_impl!(YuzuCrypt, YuzuCryptReader<T>);
1573
1574impl<R: Read> Read for YuzuCryptReader<R> {
1575 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1576 let readed = self.inner.read(buf)?;
1577 let hash = self.key ^ 0x1DDB6E7A;
1578 let mut key = (hash ^ (hash >> 8) ^ (hash >> 16) ^ (hash >> 24)) as u8;
1579 if key == 0 {
1580 key = 0xD0;
1581 }
1582 for t in (&mut buf[..readed]).iter_mut() {
1583 *t ^= key;
1584 }
1585 self.pos += readed as u64;
1586 Ok(readed)
1587 }
1588}
1589
1590seek_crypt_filehash_key_u8_impl!(HighRunningCrypt, HighRunningCryptReader<T>);
1591
1592impl<R: Read> Read for HighRunningCryptReader<R> {
1593 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1594 let readed = self.inner.read(buf)?;
1595 let key = self.key as u64;
1596 if key != 0 {
1597 for (i, t) in (&mut buf[..readed]).iter_mut().enumerate() {
1598 let offset = self.seg_start + self.pos + i as u64;
1599 if offset % key != 0 {
1600 *t ^= self.key;
1601 }
1602 }
1603 }
1604 self.pos += readed as u64;
1605 Ok(readed)
1606 }
1607}
1608
1609seek_reader_key_impl!(KissCryptReader<T>, u32);
1610
1611#[derive(Debug)]
1612pub struct PuCaCrypt {
1613 base: BaseSchema,
1614 hash_table: Vec<u32>,
1615 key_table: Vec<u8>,
1616}
1617
1618impl PuCaCrypt {
1619 pub fn new(base: BaseSchema, hash_table: Vec<u32>, key_table: Vec<u8>) -> Result<Self> {
1620 if hash_table.len() != key_table.len() {
1621 anyhow::bail!(
1622 "Hash table and key table must have the same length, but got {} and {}",
1623 hash_table.len(),
1624 key_table.len()
1625 );
1626 }
1627 Ok(Self {
1628 base,
1629 hash_table,
1630 key_table,
1631 })
1632 }
1633 fn get_key_table(&self, file_hash: u32) -> [u8; 0x400] {
1634 let mut hash_table = [0u8; 32];
1635 let mut hash = file_hash;
1636 for k in (0..32).step_by(4) {
1637 if hash & 1 != 0 {
1638 hash |= 0x80000000;
1639 } else {
1640 hash &= 0x7FFFFFFF;
1641 }
1642 hash_table[k..k + 4].copy_from_slice(&hash.to_le_bytes());
1643 hash >>= 1;
1644 }
1645 let mut key_table = [0u8; 0x400];
1646 for l in 0..32 {
1647 for m in 0..32 {
1648 key_table[l * 32 + m] = (!hash_table[l]) ^ hash_table[m];
1649 }
1650 }
1651 key_table
1652 }
1653}
1654
1655impl Crypt for PuCaCrypt {
1656 base_schema_impl!();
1657 fn decrypt_supported(&self) -> bool {
1658 true
1659 }
1660 fn decrypt_seek_supported(&self) -> bool {
1661 true
1662 }
1663 fn decrypt<'a>(
1664 &self,
1665 entry: &Xp3Entry,
1666 cur_seg: &Segment,
1667 stream: Box<dyn Read + Send + Sync + 'a>,
1668 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
1669 if let Some(pos) = self.hash_table.iter().position(|&h| h == entry.file_hash) {
1670 Ok(Box::new(PuCaCryptReader::new(
1671 stream,
1672 cur_seg,
1673 self.key_table[pos],
1674 )))
1675 } else {
1676 Ok(Box::new(PuCaCryptReader2::new(
1677 stream,
1678 cur_seg,
1679 self.get_key_table(entry.file_hash),
1680 )))
1681 }
1682 }
1683 fn decrypt_with_seek<'a>(
1684 &self,
1685 entry: &Xp3Entry,
1686 cur_seg: &Segment,
1687 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
1688 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
1689 if let Some(pos) = self.hash_table.iter().position(|&h| h == entry.file_hash) {
1690 Ok(Box::new(PuCaCryptReader::new(
1691 stream,
1692 cur_seg,
1693 self.key_table[pos],
1694 )))
1695 } else {
1696 Ok(Box::new(PuCaCryptReader2::new(
1697 stream,
1698 cur_seg,
1699 self.get_key_table(entry.file_hash),
1700 )))
1701 }
1702 }
1703}
1704
1705seek_reader_key_impl!(PuCaCryptReader<T>, u8);
1706
1707impl<T: Read> Read for PuCaCryptReader<T> {
1708 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1709 let readed = self.inner.read(buf)?;
1710 for t in (&mut buf[..readed]).iter_mut() {
1711 *t ^= self.key;
1712 }
1713 self.pos += readed as u64;
1714 Ok(readed)
1715 }
1716}
1717
1718seek_reader_key_impl!(PuCaCryptReader2<T>, [u8; 0x400]);
1719
1720impl<R: Read> Read for PuCaCryptReader2<R> {
1721 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1722 let readed = self.inner.read(buf)?;
1723 let mut offset = ((self.seg_start + self.pos) & 0x3FF) as usize;
1724 for t in (&mut buf[..readed]).iter_mut() {
1725 *t ^= self.key[offset];
1726 offset = (offset + 1) & 0x3FF;
1727 }
1728 self.pos += readed as u64;
1729 Ok(readed)
1730 }
1731}
1732
1733#[derive(Debug)]
1734pub struct RhapsodyCrypt {
1735 base: BaseSchema,
1736 names: HashMap<u32, String>,
1737}
1738
1739impl RhapsodyCrypt {
1740 pub fn new(
1741 base: BaseSchema,
1742 file_list_name: &str,
1743 file_list_path: Option<&str>,
1744 ) -> Result<Self> {
1745 let file_list = if let Some(path) = file_list_path {
1746 std::fs::read_to_string(path)?
1747 } else {
1748 query_filename_list(file_list_name)?
1749 };
1750 let mut names = HashMap::new();
1751 for name in file_list.lines() {
1752 let name = name.trim();
1753 if !name.is_empty() {
1754 names.insert(Self::get_name_hash(name.chars()), name.to_string());
1755 }
1756 }
1757 Ok(Self { base, names })
1758 }
1759 fn get_name_hash<T: Iterator<Item = char>>(name: T) -> u32 {
1760 let mut hash = 0;
1761 for c in name {
1762 hash = Self::update_name_hash(hash, c);
1763 }
1764 hash
1765 }
1766 const fn update_name_hash(hash: u32, c: char) -> u32 {
1767 let c = c.to_ascii_lowercase() as u32;
1768 let mut hash = w!(0x1000193u32 * hash ^ (c & 0xFF));
1769 hash = w!(0x1000193u32 * hash ^ ((c >> 8) & 0xFF));
1770 hash
1771 }
1772 fn get_key(&self, hash: u32) -> [u8; 12] {
1773 let mut key = [0u8; 12];
1774 key[0..4].copy_from_slice(&hash.to_le_bytes());
1775 key[4..8].copy_from_slice(&(0x6E1DA9B2u32).to_le_bytes());
1776 key[8..12].copy_from_slice(&(0x0040C800u32).to_le_bytes());
1777 key
1778 }
1779}
1780
1781impl Crypt for RhapsodyCrypt {
1782 base_schema_impl!();
1783 fn read_name<'a>(&self, reader: &mut Box<dyn Read + 'a>) -> Result<(String, u64)> {
1784 use msg_tool_macro::rhapsody_crypt_const_name_hash as hash;
1785 const PNG_HASH: u32 = hash!(".png");
1786 const MAP_HASH: u32 = hash!(".map");
1787 const ASD_HASH: u32 = hash!(".asd");
1788 const TJS_HASH: u32 = hash!(".tjs");
1789 const TXT_HASH: u32 = hash!(".txt");
1790 const KS_HASH: u32 = hash!(".ks");
1791 const WAV_HASH: u32 = hash!(".wav");
1792 const JPG_HASH: u32 = hash!(".jpg");
1793 const OGG_HASH: u32 = hash!(".ogg");
1794 let key = reader.read_u32()?;
1795 let name_hash = reader.read_u32()? ^ key;
1796 if let Some(name) = self.names.get(&name_hash) {
1797 return Ok((name.clone(), 8));
1798 }
1799 let ext_hash = reader.read_u32()? ^ key;
1800 let mut name = format!("{:08X}", name_hash);
1801 match ext_hash {
1802 PNG_HASH => name += ".png",
1803 MAP_HASH => name += ".map",
1804 ASD_HASH => name += ".asd",
1805 TJS_HASH => name += ".tjs",
1806 TXT_HASH => name += ".txt",
1807 KS_HASH => name += ".ks",
1808 WAV_HASH => name += ".wav",
1809 JPG_HASH => name += ".jpg",
1810 OGG_HASH => name += ".ogg",
1811 _ => name += format!(".{:08X}", ext_hash).as_str(),
1812 };
1813 Ok((name, 12))
1814 }
1815 fn decrypt_supported(&self) -> bool {
1816 true
1817 }
1818 fn decrypt_seek_supported(&self) -> bool {
1819 true
1820 }
1821 fn decrypt<'a>(
1822 &self,
1823 entry: &Xp3Entry,
1824 cur_seg: &Segment,
1825 stream: Box<dyn Read + Send + Sync + 'a>,
1826 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
1827 Ok(Box::new(RhapsodyCryptReader::new(
1828 stream,
1829 cur_seg,
1830 self.get_key(entry.file_hash),
1831 )))
1832 }
1833 fn decrypt_with_seek<'a>(
1834 &self,
1835 entry: &Xp3Entry,
1836 cur_seg: &Segment,
1837 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
1838 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
1839 Ok(Box::new(RhapsodyCryptReader::new(
1840 stream,
1841 cur_seg,
1842 self.get_key(entry.file_hash),
1843 )))
1844 }
1845}
1846
1847seek_reader_key_impl!(RhapsodyCryptReader<T>, [u8; 12]);
1848
1849impl<R: Read> Read for RhapsodyCryptReader<R> {
1850 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1851 let readed = self.inner.read(buf)?;
1852 let mut offset = ((self.seg_start + self.pos) % 12) as usize;
1853 for t in (&mut buf[..readed]).iter_mut() {
1854 *t ^= self.key[offset];
1855 offset = (offset + 1) % 12;
1856 }
1857 self.pos += readed as u64;
1858 Ok(readed)
1859 }
1860}
1861
1862#[derive(Debug)]
1863pub struct MadoCrypt {
1864 base: AkabeiCrypt,
1865}
1866
1867impl MadoCrypt {
1868 pub fn new(base: BaseSchema, seed: u32) -> Self {
1869 Self {
1870 base: AkabeiCrypt::new(base, seed),
1871 }
1872 }
1873}
1874
1875impl AsRef<BaseSchema> for MadoCrypt {
1876 fn as_ref(&self) -> &BaseSchema {
1877 &self.base.base
1878 }
1879}
1880
1881impl Crypt for MadoCrypt {
1882 base_schema2_impl!();
1883 fn decrypt_supported(&self) -> bool {
1884 true
1885 }
1886 fn decrypt_seek_supported(&self) -> bool {
1887 true
1888 }
1889 fn decrypt<'a>(
1890 &self,
1891 entry: &Xp3Entry,
1892 cur_seg: &Segment,
1893 stream: Box<dyn Read + Send + Sync + 'a>,
1894 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
1895 Ok(Box::new(MadoCryptReader::new(
1896 stream,
1897 cur_seg,
1898 self.base.get_key(entry.file_hash),
1899 )))
1900 }
1901 fn decrypt_with_seek<'a>(
1902 &self,
1903 entry: &Xp3Entry,
1904 cur_seg: &Segment,
1905 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
1906 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
1907 Ok(Box::new(MadoCryptReader::new(
1908 stream,
1909 cur_seg,
1910 self.base.get_key(entry.file_hash),
1911 )))
1912 }
1913}
1914
1915seek_reader_key_impl!(MadoCryptReader<T>, [u8; 0x20]);
1916
1917impl<R: Read> Read for MadoCryptReader<R> {
1918 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
1919 let readed = self.inner.read(buf)?;
1920 for (i, t) in (&mut buf[..readed]).iter_mut().enumerate() {
1921 let offset = self.seg_start + self.pos + i as u64;
1922 *t ^= self.key[(offset % 0x1F) as usize];
1923 }
1924 self.pos += readed as u64;
1925 Ok(readed)
1926 }
1927}
1928
1929#[derive(Debug)]
1930pub struct SmxCrypt {
1931 base: BaseSchema,
1932 mask: u32,
1933 key_seq: Vec<u8>,
1934}
1935
1936impl SmxCrypt {
1937 pub fn new(base: BaseSchema, mask: u32, key_seq: &[u8]) -> Result<Self> {
1938 if key_seq.len() <= mask as usize + 1 {
1939 anyhow::bail!(
1940 "Key sequence length must be greater than mask + 1, but got {} and {}",
1941 key_seq.len(),
1942 mask
1943 );
1944 }
1945 if key_seq.len() < 2 {
1946 anyhow::bail!(
1947 "Key sequence length must be at least 2, but got {}",
1948 key_seq.len()
1949 );
1950 }
1951 Ok(Self {
1952 base,
1953 mask,
1954 key_seq: key_seq.to_vec(),
1955 })
1956 }
1957
1958 fn generate_key(&self, file_hash: u32) -> Vec<u8> {
1959 let mut key = vec![0u8; self.key_seq.len() - 1];
1960 for i in 1..self.key_seq.len() {
1961 key[i - 1] = (file_hash >> self.key_seq[i]) as u8;
1962 }
1963 key
1964 }
1965}
1966
1967impl Crypt for SmxCrypt {
1968 base_schema_impl!();
1969 fn decrypt_supported(&self) -> bool {
1970 true
1971 }
1972 fn decrypt_seek_supported(&self) -> bool {
1973 true
1974 }
1975 fn decrypt<'a>(
1976 &self,
1977 entry: &Xp3Entry,
1978 cur_seg: &Segment,
1979 stream: Box<dyn Read + Send + Sync + 'a>,
1980 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
1981 let start_key = (entry.file_hash >> self.key_seq[0]) as u8;
1982 Ok(Box::new(SmxCryptReader::new(
1983 stream,
1984 cur_seg,
1985 (self.mask, start_key, self.generate_key(entry.file_hash)),
1986 )))
1987 }
1988 fn decrypt_with_seek<'a>(
1989 &self,
1990 entry: &Xp3Entry,
1991 cur_seg: &Segment,
1992 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
1993 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
1994 let start_key = (entry.file_hash >> self.key_seq[0]) as u8;
1995 Ok(Box::new(SmxCryptReader::new(
1996 stream,
1997 cur_seg,
1998 (self.mask, start_key, self.generate_key(entry.file_hash)),
1999 )))
2000 }
2001}
2002
2003seek_reader_key_impl!(SmxCryptReader<T>, (u32, u8, Vec<u8>));
2004
2005impl<R: Read> Read for SmxCryptReader<R> {
2006 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2007 let readed = self.inner.read(buf)?;
2008 let (mask, start_key, key) = &self.key;
2009 let mask = *mask as u64;
2010 let mut offset = self.seg_start + self.pos;
2011 for t in (&mut buf[..readed]).iter_mut() {
2012 let key = if offset <= 100 {
2013 *start_key
2014 } else {
2015 key[(offset & mask) as usize]
2016 };
2017 *t ^= key;
2018 offset += 1;
2019 }
2020 self.pos += readed as u64;
2021 Ok(readed)
2022 }
2023}
2024
2025seek_crypt_filehash_key_impl!(FestivalCrypt, FestivalCryptReader<T>);
2026
2027impl<R: Read> Read for FestivalCryptReader<R> {
2028 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2029 let readed = self.inner.read(buf)?;
2030 let key = (!(self.key >> 7)) as u8;
2031 for t in (&mut buf[..readed]).iter_mut() {
2032 *t ^= key;
2033 }
2034 self.pos += readed as u64;
2035 Ok(readed)
2036 }
2037}
2038
2039seek_crypt_impl!(PinPointCrypt, PinPointCryptReader<T>);
2040
2041impl<R: Read> PinPointCryptReader<R> {
2042 #[inline(always)]
2043 fn count_set_bits(x: u8) -> u32 {
2044 let mut bit_count = ((x & 0x55) + ((x >> 1) & 0x55)) as u32;
2045 bit_count = (bit_count & 0x33) + ((bit_count >> 2) & 0x33);
2046 ((bit_count & 0xF) + ((bit_count >> 4) & 0xF)) & 0xF
2047 }
2048}
2049
2050impl<R: Read> Read for PinPointCryptReader<R> {
2051 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2052 let readed = self.inner.read(buf)?;
2053 for t in (&mut buf[..readed]).iter_mut() {
2054 let bit_count = Self::count_set_bits(*t);
2055 if bit_count > 0 {
2056 *t = (*t).rotate_left(bit_count);
2057 }
2058 }
2059 self.pos += readed as u64;
2060 Ok(readed)
2061 }
2062}
2063
2064seek_crypt_filehash_key_impl!(HybridCrypt, HybridCryptReader<T>);
2065
2066impl<R: Read> Read for HybridCryptReader<R> {
2067 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2068 let readed = self.inner.read(buf)?;
2069 let key = (self.key >> 5) as u8;
2070 for t in (&mut buf[..readed]).iter_mut() {
2071 *t ^= key;
2072 }
2073 self.pos += readed as u64;
2074 Ok(readed)
2075 }
2076}
2077
2078#[derive(Debug)]
2079pub struct NekoWorksCrypt {
2080 base: BaseSchema,
2081 key: Vec<u8>,
2082}
2083
2084impl NekoWorksCrypt {
2085 pub fn new(base: BaseSchema, key: Vec<u8>) -> Result<Self> {
2086 if key.len() < 31 {
2087 anyhow::bail!("NekoWorksCrypt: key is too small.");
2088 }
2089 Ok(Self { base, key })
2090 }
2091
2092 fn init_key(&self, mut hash: u32) -> [u8; 31] {
2093 hash &= 0x7FFFFFFF;
2094 hash = hash << 31 | hash;
2095 let mut key = [0; 31];
2096 key.copy_from_slice(&self.key[..31]);
2097 for i in 0..31 {
2098 key[i] ^= hash as u8;
2099 hash = (hash & 0xFFFFFFFE) << 23 | hash >> 8;
2100 }
2101 key
2102 }
2103}
2104
2105impl Crypt for NekoWorksCrypt {
2106 base_schema_impl!();
2107 fn decrypt_supported(&self) -> bool {
2108 true
2109 }
2110 fn decrypt_seek_supported(&self) -> bool {
2111 true
2112 }
2113 fn decrypt<'a>(
2114 &self,
2115 entry: &Xp3Entry,
2116 cur_seg: &Segment,
2117 stream: Box<dyn Read + Send + Sync + 'a>,
2118 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
2119 Ok(Box::new(NekoWorksCryptReader::new(
2120 stream,
2121 cur_seg,
2122 self.init_key(entry.file_hash),
2123 )))
2124 }
2125 fn decrypt_with_seek<'a>(
2126 &self,
2127 entry: &Xp3Entry,
2128 cur_seg: &Segment,
2129 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
2130 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
2131 Ok(Box::new(NekoWorksCryptReader::new(
2132 stream,
2133 cur_seg,
2134 self.init_key(entry.file_hash),
2135 )))
2136 }
2137}
2138
2139seek_reader_key_impl!(NekoWorksCryptReader<T>, [u8; 31]);
2140
2141impl<R: Read> Read for NekoWorksCryptReader<R> {
2142 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2143 let readed = self.inner.read(buf)?;
2144 let mut offset = ((self.seg_start + self.pos) % 31) as usize;
2145 for t in (&mut buf[..readed]).iter_mut() {
2146 *t ^= self.key[offset];
2147 offset = (offset + 1) % 31;
2148 }
2149 self.pos += readed as u64;
2150 Ok(readed)
2151 }
2152}
2153
2154#[derive(Debug)]
2155pub struct NinkiSeiyuuCrypt {
2156 base: BaseSchema,
2157 tbl2: [u8; 64],
2158 tbl3: [u8; 64],
2159}
2160
2161impl NinkiSeiyuuCrypt {
2162 pub fn new(base: BaseSchema, key1: u64, key2: u64, key3: u64) -> Self {
2163 let tbl2 = Self::get_table2(3080);
2164 let tbl3 = Self::get_table3(3080, key1, key2, key3);
2165 Self { base, tbl2, tbl3 }
2166 }
2167 fn get_table1(seed: u32) -> [u8; 32] {
2168 let mut key = [0; 32];
2169 let mut v48 = seed & 0x7FFFFFFF;
2170 for i in 0..31 {
2171 key[i] = v48 as u8;
2172 v48 = (v48 >> 8) | (((v48 as u8) as u32) << 23);
2173 }
2174 key
2175 }
2176 fn get_table2(seed: u32) -> [u8; 64] {
2177 let mut key = [0; 64];
2178 let v51 = seed & 0xFFF;
2179 let v52 = ((v51 | (v51 << 13)) as u64) | (((v51 >> 19) as u64) << 32);
2180 let mut v53 = v51 | ((v52 as u32) << 13);
2181 let mut v54 = (((((v52 as u32) << 7) & 0x1FFFFFFF) as u64) | (v52 >> 19)) as u32;
2182 for i in 0..61 {
2183 let v56 = v53 as u8;
2184 key[i] = v56;
2185 v53 = ((((v54 as u64) << 32) | (v53 as u64)) >> 8) as u32;
2186 v54 = (v54 >> 8) | ((v56 as u32) << 21);
2187 }
2188 key
2189 }
2190 fn get_table3(seed: u32, key1: u64, key2: u64, key3: u64) -> [u8; 64] {
2191 let mut key = [0; 64];
2192 let v88 = seed & 0xFFF;
2193 let v89 = ((v88 | (v88 << 13)) as u64) | (((v88 >> 19) as u64) << 32);
2194 let mut v90 = ((key1 + key2) ^ ((v88 | ((v89 as u32) << 13)) as u64)) as u32;
2195 let mut v91 =
2196 ((((key1 + ((key3 & 0xFFFFFFFF00000000) | (key2 & 0xFFFFFFFF))) >> 32) & 0x1FFFFFFF)
2197 ^ (((((v89 as u32) << 7) & 0x1FFFFFFF) as u64) | (v89 >> 19))) as u32;
2198 for i in 0..61 {
2199 let v93 = v90 as u8;
2200 key[i] = v93;
2201 v90 = ((((v91 as u64) << 32) | (v90 as u64)) >> 8) as u32;
2202 v91 = (v91 >> 8) | ((v93 as u32) << 21);
2203 }
2204 key
2205 }
2206}
2207
2208impl Crypt for NinkiSeiyuuCrypt {
2209 base_schema_impl!();
2210 fn decrypt_supported(&self) -> bool {
2211 true
2212 }
2213 fn decrypt_seek_supported(&self) -> bool {
2214 true
2215 }
2216 fn decrypt<'a>(
2217 &self,
2218 entry: &Xp3Entry,
2219 cur_seg: &Segment,
2220 stream: Box<dyn Read + Send + Sync + 'a>,
2221 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
2222 let key = (
2223 Self::get_table1(entry.file_hash),
2224 self.tbl2.clone(),
2225 self.tbl3.clone(),
2226 );
2227 Ok(Box::new(NinkiSeiyuuCryptReader::new(stream, cur_seg, key)))
2228 }
2229 fn decrypt_with_seek<'a>(
2230 &self,
2231 entry: &Xp3Entry,
2232 cur_seg: &Segment,
2233 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
2234 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
2235 let key = (
2236 Self::get_table1(entry.file_hash),
2237 self.tbl2.clone(),
2238 self.tbl3.clone(),
2239 );
2240 Ok(Box::new(NinkiSeiyuuCryptReader::new(stream, cur_seg, key)))
2241 }
2242}
2243
2244seek_reader_key_impl!(NinkiSeiyuuCryptReader<T>, ([u8; 32], [u8; 64], [u8; 64]));
2245
2246impl<R: Read> Read for NinkiSeiyuuCryptReader<R> {
2247 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2248 let readed = self.inner.read(buf)?;
2249 let mut offset1 = ((self.seg_start + self.pos) % 0x1F) as usize;
2250 let mut offset2 = ((self.seg_start + self.pos) % 0x3D) as usize;
2251 for t in (&mut buf[..readed]).iter_mut() {
2252 *t ^= self.key.0[offset1];
2253 *t = (*t).wrapping_add(self.key.1[offset2] ^ self.key.2[offset2]);
2254 offset1 = (offset1 + 1) % 0x1F;
2255 offset2 = (offset2 + 1) % 0x3D;
2256 }
2257 self.pos += readed as u64;
2258 Ok(readed)
2259 }
2260}
2261
2262#[derive(Debug)]
2263pub struct SyangrilaSmartCrypt {
2264 base: BaseSchema,
2265}
2266
2267impl SyangrilaSmartCrypt {
2268 pub fn new(base: BaseSchema) -> Self {
2269 Self { base }
2270 }
2271
2272 fn get_key(hash: u32) -> [u8; 5] {
2273 [
2274 (hash >> 5) as u8,
2275 (hash >> 5) as u8,
2276 (hash >> 7) as u8,
2277 (hash >> 1) as u8,
2278 (hash >> 4) as u8,
2279 ]
2280 }
2281}
2282
2283impl Crypt for SyangrilaSmartCrypt {
2284 base_schema_impl!();
2285 fn decrypt_supported(&self) -> bool {
2286 true
2287 }
2288 fn decrypt_seek_supported(&self) -> bool {
2289 true
2290 }
2291 fn decrypt<'a>(
2292 &self,
2293 entry: &Xp3Entry,
2294 cur_seg: &Segment,
2295 stream: Box<dyn Read + Send + Sync + 'a>,
2296 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
2297 Ok(Box::new(SyangrilaSmartCryptReader::new(
2298 stream,
2299 cur_seg,
2300 Self::get_key(entry.file_hash),
2301 )))
2302 }
2303 fn decrypt_with_seek<'a>(
2304 &self,
2305 entry: &Xp3Entry,
2306 cur_seg: &Segment,
2307 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
2308 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
2309 Ok(Box::new(SyangrilaSmartCryptReader::new(
2310 stream,
2311 cur_seg,
2312 Self::get_key(entry.file_hash),
2313 )))
2314 }
2315}
2316
2317seek_reader_key_impl!(SyangrilaSmartCryptReader<T>, [u8; 5]);
2318
2319impl<R: Read> Read for SyangrilaSmartCryptReader<R> {
2320 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2321 let readed = self.inner.read(buf)?;
2322 let offset = self.seg_start + self.pos;
2323 for (i, t) in buf[..readed].iter_mut().enumerate() {
2324 let tpos = offset + i as u64;
2325 if tpos <= 0x64 {
2326 *t ^= self.key[4];
2327 } else {
2328 *t ^= self.key[(tpos & 3) as usize];
2329 }
2330 }
2331 self.pos += readed as u64;
2332 Ok(readed)
2333 }
2334}
2335
2336const WARC_MAGIC: &[u8] = b"warc";
2337const WARC_TYPE_KEY: [u8; 3] = [0x27, 0xaf, 0x67];
2338const WARC_SIZE_KEY: [u8; 4] = [0x7d, 0x16, 0x9f, 0xf1];
2339
2340#[derive(Debug)]
2341pub struct Kano2Crypt {
2342 base: BaseSchema,
2343}
2344
2345impl Kano2Crypt {
2346 pub fn new(base: BaseSchema) -> Self {
2347 Self { base }
2348 }
2349}
2350
2351impl Crypt for Kano2Crypt {
2352 base_schema_impl!();
2353 fn decrypt_supported(&self) -> bool {
2354 true
2355 }
2356 fn decrypt_seek_supported(&self) -> bool {
2357 true
2358 }
2359 fn decrypt<'a>(
2360 &self,
2361 entry: &Xp3Entry,
2362 cur_seg: &Segment,
2363 stream: Box<dyn Read + Send + Sync + 'a>,
2364 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
2365 Ok(Box::new(Kano2CryptReader::new(
2366 stream,
2367 cur_seg,
2368 entry.file_hash as u8,
2369 )))
2370 }
2371 fn decrypt_with_seek<'a>(
2372 &self,
2373 entry: &Xp3Entry,
2374 cur_seg: &Segment,
2375 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
2376 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
2377 Ok(Box::new(Kano2CryptReader::new(
2378 stream,
2379 cur_seg,
2380 entry.file_hash as u8,
2381 )))
2382 }
2383 fn need_filter(&self, _filename: &str, buf: &[u8], buf_len: usize) -> bool {
2384 buf_len >= 4 && buf.starts_with(WARC_MAGIC)
2385 }
2386 fn filter<'a>(&self, mut entry: Entry<'a>) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
2387 let mut magic = [0; 4];
2388 entry.read_exact(&mut magic)?;
2389 if &magic != WARC_MAGIC {
2390 anyhow::bail!("Unsupported magic: {:?}.", magic);
2391 }
2392 let mut typ = [0; 3];
2393 entry.read_exact(&mut typ)?;
2394 for i in 0..3 {
2395 typ[i] ^= WARC_TYPE_KEY[i];
2396 }
2397 if &typ != b"STR" && &typ != b"OCT" && &typ != b"AOD" && &typ != b"INT" && &typ != b"REL" {
2398 eprintln!("WARNING: Unknown type key: {:?}", typ);
2399 crate::COUNTER.inc_warning();
2400 }
2401 let mut size = [0; 4];
2402 entry.read_exact(&mut size)?;
2403 for i in 0..4 {
2404 size[i] ^= typ[0] ^ WARC_SIZE_KEY[i];
2405 }
2406 let _uncompressed_size = u32::from_le_bytes(size);
2407 let reader = flate2::read::ZlibDecoder::new(entry);
2408 if &typ == b"STR" {
2409 return Ok(Box::new(PrefixStream::new(vec![0xFF, 0xFE], reader)));
2410 }
2411 Ok(Box::new(reader))
2412 }
2413}
2414
2415seek_reader_key_impl!(Kano2CryptReader<T>, u8);
2416
2417impl<R: Read> Read for Kano2CryptReader<R> {
2418 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2419 let readed = self.inner.read(buf)?;
2420 let mut offset = (self.seg_start + self.pos) % 8;
2421 for t in buf[..readed].iter_mut() {
2422 if offset == 0 {
2423 *t ^= self.key;
2424 }
2425 offset = (offset + 1) % 8;
2426 }
2427 self.pos += readed as u64;
2428 Ok(readed)
2429 }
2430}
2431
2432#[derive(Debug)]
2433pub struct MiburoCrypt {
2434 base: BaseSchema,
2435}
2436
2437impl MiburoCrypt {
2438 pub fn new(base: BaseSchema) -> Self {
2439 Self { base }
2440 }
2441
2442 fn init_key(mut hash: u32) -> [u8; 29] {
2443 hash &= 0x1FFFFFFF;
2444 hash |= (hash & 1) << 29;
2445 let mut key = [0; 29];
2446 for i in 0..29 {
2447 key[i] = hash as u8;
2448 hash = (hash >> 8) | ((hash << 0x15) & 0xFF000000);
2449 }
2450 key
2451 }
2452}
2453
2454impl Crypt for MiburoCrypt {
2455 base_schema_impl!();
2456 fn decrypt_supported(&self) -> bool {
2457 true
2458 }
2459 fn decrypt_seek_supported(&self) -> bool {
2460 true
2461 }
2462 fn decrypt<'a>(
2463 &self,
2464 entry: &Xp3Entry,
2465 cur_seg: &Segment,
2466 stream: Box<dyn Read + Send + Sync + 'a>,
2467 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
2468 Ok(Box::new(MiburoCryptReader::new(
2469 stream,
2470 cur_seg,
2471 Self::init_key(entry.file_hash),
2472 )))
2473 }
2474 fn decrypt_with_seek<'a>(
2475 &self,
2476 entry: &Xp3Entry,
2477 cur_seg: &Segment,
2478 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
2479 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
2480 Ok(Box::new(MiburoCryptReader::new(
2481 stream,
2482 cur_seg,
2483 Self::init_key(entry.file_hash),
2484 )))
2485 }
2486}
2487
2488seek_reader_key_impl!(MiburoCryptReader<T>, [u8; 29]);
2489
2490impl<R: Read> Read for MiburoCryptReader<R> {
2491 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2492 let readed = self.inner.read(buf)?;
2493 let mut offset = ((self.seg_start + self.pos) % 29) as usize;
2494 for t in buf[..readed].iter_mut() {
2495 *t ^= self.key[offset];
2496 offset = (offset + 1) % 29;
2497 }
2498 self.pos += readed as u64;
2499 Ok(readed)
2500 }
2501}
2502
2503#[derive(Debug)]
2504pub struct PureMoreCrypt {
2505 base: BaseSchema,
2506 names: HashMap<String, String>,
2507}
2508
2509impl PureMoreCrypt {
2510 pub fn new(
2511 base: BaseSchema,
2512 file_list_name: &str,
2513 file_list_path: Option<&str>,
2514 char_map: &str,
2515 layer_name_suffix: &str,
2516 ) -> Result<Self> {
2517 use sha2::Digest;
2518 use unicode_segmentation::UnicodeSegmentation;
2519 let mut names = HashMap::new();
2520 let file_list = if let Some(path) = file_list_path {
2521 std::fs::read_to_string(path)?
2522 } else {
2523 query_filename_list(file_list_name)?
2524 };
2525 let char_map: Vec<_> = char_map.graphemes(true).collect();
2526 for name in file_list.lines() {
2527 let name = name.trim();
2528 if name.is_empty() {
2529 continue;
2530 }
2531 let parts: Vec<_> = name.splitn(2, ",").collect();
2532 let mut name = parts[0].to_owned();
2533 if let Some(ext) = name.rfind(".") {
2534 name = name[..ext].to_owned();
2535 }
2536 if parts.len() == 2 {
2537 name += layer_name_suffix;
2538 }
2539 let encoded = encode_string(Encoding::Utf16LE, &name, true)?;
2540 let mut sha256 = sha2::Sha256::new();
2541 sha256.update(&encoded);
2542 name = String::new();
2543 for hash in sha256.finalize() {
2544 name += char_map[hash as usize];
2545 }
2546 name += ".tlg";
2547 names.insert(name, parts[0].to_owned());
2548 }
2549 Ok(Self { base, names })
2550 }
2551}
2552
2553impl Crypt for PureMoreCrypt {
2554 base_schema_impl!();
2555 fn read_name<'a>(&self, reader: &mut Box<dyn Read + 'a>) -> Result<(String, u64)> {
2556 let (name, size) = default_read_name(reader)?;
2557 let name_length = name.encode_utf16().count();
2558 if name_length != 36 || !name.ends_with(".tlg") {
2559 return Ok((name, size));
2560 }
2561 Ok((
2562 self.names.get(&name).map(|s| s.to_string()).unwrap_or(name),
2563 size,
2564 ))
2565 }
2566 fn init(&self, archive: &mut Xp3Archive) -> Result<()> {
2567 for entry in archive.entries.iter_mut() {
2568 entry.flags = 0;
2570 }
2571 Ok(())
2572 }
2573}
2574
2575#[derive(Debug)]
2576pub struct Xor2Crypt {
2577 base: BaseSchema,
2578 key1: u8,
2579 key2: u8,
2580}
2581
2582impl Xor2Crypt {
2583 pub fn new(base: BaseSchema, key1: u8, key2: u8) -> Self {
2584 Self { base, key1, key2 }
2585 }
2586}
2587
2588impl Crypt for Xor2Crypt {
2589 base_schema_impl!();
2590 fn decrypt_supported(&self) -> bool {
2591 true
2592 }
2593 fn decrypt_seek_supported(&self) -> bool {
2594 true
2595 }
2596 fn decrypt<'a>(
2597 &self,
2598 _entry: &Xp3Entry,
2599 cur_seg: &Segment,
2600 stream: Box<dyn Read + Send + Sync + 'a>,
2601 ) -> Result<Box<dyn ReadDebug + Send + Sync + 'a>> {
2602 Ok(Box::new(Xor2CryptReader::new(
2603 stream,
2604 cur_seg,
2605 (self.key1, self.key2),
2606 )))
2607 }
2608 fn decrypt_with_seek<'a>(
2609 &self,
2610 _entry: &Xp3Entry,
2611 cur_seg: &Segment,
2612 stream: Box<dyn ReadSeek + Send + Sync + 'a>,
2613 ) -> Result<Box<dyn ReadSeek + Send + Sync + 'a>> {
2614 Ok(Box::new(Xor2CryptReader::new(
2615 stream,
2616 cur_seg,
2617 (self.key1, self.key2),
2618 )))
2619 }
2620}
2621
2622seek_reader_key_impl!(Xor2CryptReader<T>, (u8, u8));
2623
2624impl<R: Read> Read for Xor2CryptReader<R> {
2625 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2626 let readed = self.inner.read(buf)?;
2627 let mut offset = self.seg_start + self.pos;
2628 for t in (&mut buf[..readed]).iter_mut() {
2629 *t ^= if offset % 2 == 0 {
2630 self.key.0
2631 } else {
2632 self.key.1
2633 };
2634 offset += 1;
2635 }
2636 self.pos += readed as u64;
2637 Ok(readed)
2638 }
2639}
2640
2641seek_crypt_filehash_key_u8_impl!(LeaveSLeaveCrypt, LeaveSLeaveCryptReader<T>);
2642
2643impl<R: Read> Read for LeaveSLeaveCryptReader<R> {
2644 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2645 let readed = self.inner.read(buf)?;
2646 for t in (&mut buf[..readed]).iter_mut() {
2647 let b = *t ^ self.key;
2648 *t = (b << 4) | (b >> 4);
2649 }
2650 self.pos += readed as u64;
2651 Ok(readed)
2652 }
2653}
2654
2655seek_reader_key_impl!(ChainReactionCryptReader<T>, (u32, u32));
2656seek_reader_key_impl!(XanaduCryptReader<T>, (u32, u32));
2657seek_reader_key_impl!(SisMikoCryptReader<T>, (u32, u32));
2658seek_reader_key_impl!(NVLCryptReader<T>, [u8; 12]);
2659
2660#[test]
2661fn test_deserialize_crypt() {
2662 for (key, schema) in CRYPT_SCHEMA.iter() {
2663 println!("Title: {}, Schema: {:?}", key, schema);
2664 }
2665 assert!(CRYPT_SCHEMA.contains_key(CIS::from_str("PURELY x CATION")));
2666}
2667
2668#[test]
2669fn check_alias_exists() {
2670 let mut alias = std::collections::HashSet::new();
2671 for (key, schema) in CRYPT_SCHEMA.iter() {
2672 if alias.contains(key.as_str()) {
2673 panic!("Game {} is already used", key);
2674 }
2675 alias.insert(key.to_string());
2676 if let Some(title) = &schema.title {
2677 for part in title.split("|") {
2678 let part = part.trim();
2679 if alias.contains(part) {
2680 panic!("Game alias {} in {} is already used", part, key);
2681 }
2682 alias.insert(part.to_string());
2683 }
2684 }
2685 }
2686}
2687
2688#[test]
2689fn test_cx_cb_table() {
2690 for (key, list) in CX_CB_TABLE.iter() {
2691 println!("Key: {}, List length: {}", key, list.len());
2692 }
2693}
2694
2695#[test]
2696fn test_altered_pink_key_table() {
2697 assert_eq!(ALTERED_PINK_KEY_TABLE.len(), 0x100);
2698}